﻿namespace CPL
{
    using System;
    using System.Collections.Generic;
    using System.Threading;

    public static class Parallel
    {
        private static object _locker = new object();

        #region {parallel for}
        public static void For(
            int lowerBound,
            int upperBound,
            Action<int> action)
        {
            For(lowerBound,
                upperBound, 
                1,
                ParallelSettings.MaxParallelismLevel,
                action);
        }

        public static void For(
            int lowerBound,
            int upperBound,
            int step,
            int maxParallelismLevel,
            Action<int> action)
        {
            var waitHandles = new List<AutoResetEvent>();
            var ranges = Partitioner.GetRanges(
                lowerBound,
                upperBound,
                step,
                maxParallelismLevel,
                PartitionerType.Equal);

            foreach (var range in ranges)
            {
                var handle = new AutoResetEvent(false);
                var localRange = range;

                var actionBlock = new WaitCallback(
                    a =>
                    {
                        //Thread.CurrentThread.Priority = ParallelSettings.Priority;

                        for (var j = localRange.LowerBound; j < localRange.UpperBound; j += localRange.Step)
                        {
                            action.Invoke(j);
                        }
                        handle.Set();
                    });

                waitHandles.Add(handle);

                var thread = new Thread(a => actionBlock.Invoke(handle));
                thread.Start();
                //ThreadPool.QueueUserWorkItem(actionBlock);
            }

            WaitHandle.WaitAll(waitHandles.ToArray());
        } 
        #endregion

        #region {parallel invoke}
        /// <summary>
        /// Invokes the specified actions in parallel.
        /// </summary>
        /// <param name="actions">The actions.</param>
        public static void Invoke(params Action<object>[] actions)
        {
            For(0, actions.Length, i => actions[i].Invoke(i));
        }
        #endregion

        #region {parallel foreach}
        public static void ForEach<T>(
            IList<T> collection,
            Action<T> action)
        {
            ForEach(collection, ParallelSettings.MaxParallelismLevel, action);
        }

        public static void ForEach<T>(
            IList<T> collection,
            int maxParallelismLevel,
            Action<T> action)
        {
            if (collection == null)
            {
                return;
            }

            For(0, collection.Count, i => action.Invoke(collection[i]));
        } 
        #endregion
    }
}